/* NOTICE
 
    Jim - the Java Image Manager, copyright (c) 2005 - 2007 Grant Gardner 
 
    Jim is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Jim is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
    In addition, as a special exception, you are permitted to link this code
    with the 3rd party libraries required to compile this file and to
    distribute a linked combination including them. You must obey the
    GNU General Public License in all respects for all of the code except for
    those 3rd party libraries that are licenced under terms incompatible with the
    GNU General Public License.  
 
 
 NOTICE */

package au.com.lastweekend.jim.imagebase;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import au.com.lastweekend.jim.imagebase.query.CombinationCondition;
import au.com.lastweekend.jim.imagebase.query.LDIFCombinationCondition;
import au.com.lastweekend.jim.imagebase.query.LDIFCondition;
import au.com.lastweekend.jim.imagebase.query.LDIFEqualsCondition;
import au.com.lastweekend.jim.imagebase.query.QueryCondition;

/**
 * An image base that works with an LDIF file. Good for offline work, and transportable to LDAP. Possibly will be the default.
 * 
 * Note this does not attempt to read general ldif files.
 * 
 * @author grant@lastweekend.com.au
 * @version $Id: LDIFImageBase.java,v 1.1 2006/03/01 09:52:04 ggardner Exp $
 */
public class LDIFImageBase implements ImageBase {

    private Map<String, ImageBaseDirectory> _imageBaseDirectoryMap = new HashMap<String, ImageBaseDirectory>();
    private File _ldifFile;
    private TagHandlerFactory _tagHandlerFactory;
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LDIFImageBase.class);

    public LDIFImageBase(File ldifFile, TagHandlerFactory tagHandlerFactory) throws IOException {

        _ldifFile = ldifFile;
        _tagHandlerFactory = tagHandlerFactory;
        load(ldifFile);

    }

    private void load(File ldifFile) throws IOException {

        if (!ldifFile.exists()) {
            ldifFile.createNewFile();
        }

        if (!ldifFile.canWrite()) {
            throw new IOException("Can't write to " + ldifFile);
        }

        BufferedReader reader = new BufferedReader(new FileReader(ldifFile));
        String line;
        Map<String, List<String>> attributeMap = new HashMap<String, List<String>>();

        while ((line = reader.readLine()) != null) {
            String[] split = line.split(": ", 2);
            if (split.length > 1) {
                String key = split[0];
                String value = split[1];

                if ("dn".equals(key)) {
                    makeDirectory(attributeMap);

                    attributeMap.clear();
                } else {
                    List<String> valueList = attributeMap.get(key);
                    if (valueList == null) {
                        valueList = new ArrayList<String>();
                        attributeMap.put(key, valueList);
                    }
                    valueList.add(value);
                }
            }
        }
        makeDirectory(attributeMap);
        LOG.info("Loaded " + _imageBaseDirectoryMap.size() + " entries from " + ldifFile);
    } /*
         * @see au.com.lastweekend.jim.imagebase.ImageBase#findDirectory(java.lang.String)
         */

    private void makeDirectory(Map<String, List<String>> attributeMap) {

        List<String> att = attributeMap.get("objectClass");
        if (att != null && att.contains("jimPhoto")) {
            ImageBaseDirectory imageDirectory = new ImageBaseDirectory();

            att = attributeMap.get("cn");
            if (att != null && att.size() > 0) {
                imageDirectory.setFilename(att.get(0));
                _imageBaseDirectoryMap.put(att.get(0), imageDirectory);

                att = attributeMap.get("title");
                if (att != null && att.size() > 0) {
                    imageDirectory.setTitle(att.get(0));
                }
                att = attributeMap.get("keywords");
                if (att != null) {
                    imageDirectory.setKeywords(new HashSet<String>(att));
                }
                att = attributeMap.get("path");
                if (att != null) {
                    Set<File> paths = new HashSet<File>();
                    for (String path : att) {
                        File filePath = new File(path);
                        paths.add(ImageBaseDirectory.convertToImageBaseFile(filePath));
                    }
                    imageDirectory.setPaths(paths);
                }

                imageDirectory.setTagHandler(_tagHandlerFactory.makeTagHandler(this, imageDirectory));

            }

        }
    }

    public ImageBaseDirectory findDirectory(String filename) {

        ImageBaseDirectory imageBaseDirectory = _imageBaseDirectoryMap.get(filename);

        return imageBaseDirectory;
    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#storeDirectory(au.com.lastweekend.jim.imagebase.ImageBaseDirectory)
     */
    public void storeDirectory(ImageBaseDirectory imageDirectory) {

        _imageBaseDirectoryMap.put(imageDirectory.getFilename(), imageDirectory);
        write(imageDirectory);
    }

    private void write(ImageBaseDirectory imageDirectory) {

        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(_ldifFile, true));
            writer.newLine();
            writer.write("dn: " + "cn=" + imageDirectory.getFilename() + ",todo.implement.full.dn");
            writer.newLine();
            writer.write("objectClass: jimPhoto");
            writer.newLine();
            writer.write("cn: " + imageDirectory.getFilename());
            writer.newLine();
            String title = imageDirectory.getTitle();
            if (title != null) {
                writer.write("title: " + title);
                writer.newLine();
            }
            for (String keyword : imageDirectory.getKeywords()) {
                writer.write("keywords: " + keyword);
                writer.newLine();
            }

            for (File path : imageDirectory.getPaths()) {
                writer.write("path: " + path.getPath());
                writer.newLine();
            }

        } catch (IOException e) {
            LOG.error("Failed writing directory for " + imageDirectory.getFilename() + " to " + _ldifFile, e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    LOG.error("Failed closing writer ", e);
                }
            }
        }

    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#removeDirectory(au.com.lastweekend.jim.imagebase.ImageBaseDirectory)
     */
    public void removeDirectory(ImageBaseDirectory imageDirectory) {

        _imageBaseDirectoryMap.remove(imageDirectory.getFilename());
        writeAll();

    }

    private void writeAll() {

        _ldifFile.delete();
        for (ImageBaseDirectory imageDirectory : _imageBaseDirectoryMap.values()) {
            write(imageDirectory);
        }

    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#replaceTag(au.com.lastweekend.jim.imagebase.ImageBaseDirectory, int, int,
     *      java.lang.Object)
     */
    public void replaceTag(ImageBaseDirectory imageDirectory, int tagType, int formatCode, Object value) {

        write(imageDirectory);

    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#search(java.lang.String)
     */
    public Set<ImageBaseDirectory> search(String searchString) {

        return Collections.emptySet();
    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#getStoredKeywords()
     */
    public Set<String> getStoredKeywords() {

        ImageBaseDirectory jimConfigDirectory = _imageBaseDirectoryMap.get("jimConfig");
        if (jimConfigDirectory == null) {
            jimConfigDirectory = new ImageBaseDirectory();
            jimConfigDirectory.setFilename("jimConfig");
            storeDirectory(jimConfigDirectory);
            jimConfigDirectory.setTagHandler(_tagHandlerFactory.makeTagHandler(this, jimConfigDirectory));
        }

        return jimConfigDirectory.getKeywords();
    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#addKeyword(java.lang.String)
     */
    public void addKeyword(String newKeywordString) {

        ImageBaseDirectory configDirectory = _imageBaseDirectoryMap.get("jimConfig");
        getDirectoryKeywords(configDirectory).add(newKeywordString);
        write(configDirectory);

    }

    /*
     * @see au.com.lastweeSet<String> keywords = (Set<String>) imageDirectory.getObject(ImageBaseDirectory.TAG_KEYWORDS);
     *      kend.jim.imagebase.ImageBase#updateKeyword(java.lang.String, java.lang.String)
     */
    public void updateKeyword(String oldKeyword, String newKeyword) {

        for (ImageBaseDirectory imageDirectory : _imageBaseDirectoryMap.values()) {

            Set<String> keywords = getDirectoryKeywords(imageDirectory);

            Iterator<String> keywordIter = keywords.iterator();
            Set<String> newKeywordSet = new HashSet<String>();
            while (keywordIter.hasNext()) {
                String keyword = keywordIter.next();
                if (AbstractImageBase.isKeywordRelated(keyword, oldKeyword)) {
                    keywordIter.remove();
                    if (newKeyword != null) {
                        String newKeywordString = newKeyword + keyword.substring(oldKeyword.length());
                        // String newKeywordString =
                        // keyword.replaceFirst(oldKeyword, newKeyword);
                        newKeywordSet.add(newKeywordString);
                    }
                }
            }
            keywords.addAll(newKeywordSet);

        }
        writeAll();
    }

    private Set<String> getDirectoryKeywords(ImageBaseDirectory imageDirectory) {

        // Use the underlying set from imageDirectory.
        @SuppressWarnings("unchecked")
        Set<String> keywords = (Set<String>) imageDirectory.getObject(ImageBaseDirectory.TAG_KEYWORDS);
        return keywords;
    }

    /*
     * @see au.com.lastweekend.jim.imagebase.ImageBase#removeKeyword(java.lang.String)
     */
    public void removeKeyword(String oldKeyword) {

        updateKeyword(oldKeyword, null);
    }

    public QueryCondition getEqualsCondition(String attributeName, String literalValue) {

        return new LDIFEqualsCondition(attributeName, literalValue);
    }

    public Collection<ImageBaseDirectory> search(QueryCondition condition) {

        LDIFCondition ldifCondition = (LDIFCondition) condition;
        Set<ImageBaseDirectory> results = new HashSet<ImageBaseDirectory>();

        for (ImageBaseDirectory imageDirectory : _imageBaseDirectoryMap.values()) {
            if (ldifCondition.matches(imageDirectory) && !"jimConfig".equals(imageDirectory.getFilename())) {
                results.add(imageDirectory);
            }
        }

        return results;

    }

    public CombinationCondition getCombinationCondition(boolean andMatch) {

        return new LDIFCombinationCondition(andMatch);
    }

}
